//----------------------------------------------------------------------------------------------------------------------
/// \file
/// <summary>Declares the TlsSlot class.</summary>
// Copyright (c) Microsoft Corporation.  All Rights Reserved.
//----------------------------------------------------------------------------------------------------------------------
#pragma once
#include "Wex.Common.h"
#include "WexDebug.h"
#include "WexTypes.h"

namespace WEX { namespace Common {
    /// <summary>
    /// The WEX::Common::Private namespace contains internal implementation details.
    /// </summary>
    namespace Private
    {
        class WEXCOMMON_API TlsSlotImpl
        {
        template<typename T>
        friend class TlsSlot;

        private:
            TlsSlotImpl();
            ~TlsSlotImpl();
            TlsSlotImpl(const TlsSlotImpl&);
            TlsSlotImpl& operator=(const TlsSlotImpl&);

            HRESULT SetValue(void* pValue);
            HRESULT GetValue(void** ppValue) const;
            DWORD GetId() const;

            const unsigned long m_id;
        };

        /// <summary>
        /// The TlsSlot class provides RAII ownership of 'Thread Local Storage' using the Win32 API's.
        /// </summary>
        template<typename T>
        class TlsSlot
        {
        public:
            /// <summary>
            /// Initializes the TlsSlot class, which allocates a Thread Local Storage slot.
            /// </summary>
            /// <remarks>
            /// If TlsAlloc fails, TlsSlot::m_id will be set to TLS_OUT_OF_INDEXES.
            /// </remarks>
            TlsSlot()
            {
                COMPILE_TIME_CHECK_V2(sizeof(T) <= sizeof(void*),
                        "TlsSlot may only contain pointer sized objects or smaller", 
                        TlsSlot_May_Only_Contain_Pointer_Sized_Objects_Or_Smaller);
            };

            /// <summary>
            /// Destroys the TlsSlot class, and (provided that m_id) was allocated correctly, frees the Thread Local Storage slot.
            /// </summary>
            ~TlsSlot()
            {
            };

            /// <summary>
            /// Stores the given 'value' in the Thread Local Storage slot.
            /// </summary>
            /// <param name="value">The value to store. Note: This must be a pointer sized value; if it isn't, then the reinterpret_cast will fail.</param>
            /// <returns>S_OK on success, a FAILED HRESULT on failure.</returns>
            HRESULT SetValue(T value)
            {
                return m_impl.SetValue(reinterpret_cast<void*>(value));
            }

            /// <summary>
            /// Retrieves the value stored in the Thread Local Storage slot.
            /// </summary>
            /// <param name="value">A reference that receives the value</param>
            /// <returns>S_OK on success, a FAILED HRESULT on failure.</returns>
            HRESULT GetValue(T& value) const
            {
                void* pTemp = NULL;
                HRESULT hr = m_impl.GetValue(&pTemp);
                if (SUCCEEDED(hr))
                {
                    value = *reinterpret_cast<T*>(&pTemp);
                }

                return hr;
            }

            /// <summary>
            /// Retrieves the DWORD identifier for the Thread Local Storage slot represented by this class.
            /// </summary>
            /// <returns>The DWORD identifier.</returns>
            DWORD GetId() const
            {
                return m_impl.GetId();
            }

        private:
            TlsSlot(const TlsSlot&);
            TlsSlot& operator=(const TlsSlot&);

            TlsSlotImpl m_impl;
        };
    } /* namespace Private */
} /* namespace Common */ } /* namespace WEX */
